(function (window) {
    "use strict";
    $.evalFn = function (args) {
        //evalFn("funcName")
        var Fn = Function;
        return new Fn('return ' + args)();
    }
    //自定义事件
    $.fn.extend({
        onInput: function (callback) {
            var el = $(this);
            /* 当前浏览器是否支持 oninput 事件 */
            if ("oninput" in el.get(0)) {
                el.on("input", function () {
                    callback && callback(this);
                });
            } else {
                /* IE独有属性，IE9 以下版本使用 */
                el.on("propertychange", function () {
                    callback && callback(this);
                });
            }
        }
    });
    String.prototype.replaceAll = function (s1, s2) {
        // g：执行全局匹配，而不是匹配第一个后停止。
        // m：执行多行匹配
        return this.replace(new RegExp(s1, "gm"), s2);
    };

    var handle = {
            //前缀
            prefix: "data-bind",
            //触发器
            jqTrigger: $({}),
            /**
             * 全局设置绑定数据,缓存 存放在内存中
             * globalData: {
             *     页面唯一key
             *     md5(pagexxx) : {
             *         页面data属性绑定key
             *         cc:{
             *              组件绑定的数据
             *              data:object,
             *              组件名字 id
             *              widgetsId:["xxx1","xxx2","xxx3"]
             *              widgetsId:{
             *                  "widgetId":{
             *                      "id":"xxx",
             *                      "type":"input or textarea or select ..."
             *                  },
             *                  ...
             *              }
             *         },
             *         ...
             *     }
             * }
             */
            globalData: {},
            //页面信息
            currPageUrl: null,
            currPageKey: null,
            pagesInfo: {},
            //记录最后触发
            ts: 0,
            sock: {
                _sock: null,
                state: 0,//0连接关闭 1连接打开
                init: function () {
                    var options = {
                        debug: false,
                        devel: false,
                        protocols_whitelist: ['websocket', 'xdr-streaming', 'xhr-streaming', 'iframe-eventsource', 'iframe-htmlfile', 'xdr-polling', 'xhr-polling', 'iframe-xhr-polling', 'jsonp-polling']
                    };
                    this._sock = new SockJS(origin + '/listen', undefined, options);
                    this._sock.onopen = this.onopen;
                    this._sock.onmessage = this.onmessage;
                    this._sock.onclose = this.onclose;
                    //console.log("sockjs init connection server", this._sock)
                },
                onopen: function () {
                    //console.log("sock onopen");
                    handle.sock.state = 1;
                },
                onmessage: function (e) {
                    var data = e.data;
                    data = JSON.parse(data);
                    var code = data.code;
                    //console.log("sock onmessage", data);
                    //console.log("sock onmessage", code);
                    if (code === "0") {
                        //成功-点击事件
                        if (data.type === "click") {
                            var widgetId = data.widgetId;
                            if (events.callbacks[widgetId]) {
                                events.callbacks[widgetId](data);
                            }
                        }
                    } else {
                        //失败
                    }
                    delete events.callbacks[widgetId];
                },
                onclose: function () {
                    //console.log("sock onclose");
                    handle.sock.state = 0;
                },
                send: function (message) {
                    this._sock.send(message);
                }
            },
            md5: function (value) {
                return md5(value);
            },
            trigger: function (type, data) {
                return this.jqTrigger.trigger(type, data);
            },
            on: function (types, selector, data, fn, /*INTERNAL*/ one) {
                return this.jqTrigger.on(types, selector, data, fn, one);
            },
            //返回当前页面加载的 page url 做为 key
            getUrlKey: function () {
                return this.currPageKey;
            },
            //返回当前页面绑定的数据
            getData: function (key) {
                if (!key) {
                    return this.globalData[this.getUrlKey()];
                }
                return this.globalData[this.getUrlKey()][key];
            },
            //返回触发器key
            getTriggerKey: function (key) {
                return this.getUrlKey() + "-" + this.prefix + "-" + key;
            },
            set: function (key, value, widgetId) {
                if (this.globalData[this.getUrlKey()]) {
                    //console.log("globalData:", this.globalData[this.getUrlKey()]);
                    this.globalData[this.getUrlKey()][key]["value"] = value;
                    return this.globalData[this.getUrlKey()][key];
                }
                return null;
            },
            update: function (key, value, ts) {
                //该判断保证 调用 this.set 和 this.trigger 一次
                if (this.ts === ts) {
                    return;
                } else if (ts !== undefined) {
                    this.ts = ts;
                }
                var r = this.set(key, value, undefined);
                //console.log("update trigger", JSON.stringify(r));
                if (r != null) {
                    var triggerId = this.getTriggerKey(key);
                    //调用 sockjs 服务更新绑定数据
                    var syncData = {};
                    syncData[key] = r;
                    this.syncEventListen("update", syncData);
                    //值改变触发 更新 所有绑定的控件
                    this.trigger(triggerId, [key, value]);
                }
            },
            /**
             * 同步事件监听
             * @param type update:更新数据, 事件=click:点击,change:改变
             * @param data 事件发送的数据
             */
            syncEventListen: function (type, data) {
                data = {
                    "type": type,
                    "data": data
                }
                this.sock.send(JSON.stringify(data));
            },
            //只初始化一次
            docEventBind: function () {
                //console.log("初始化 document on 绑定事件");
                $(document).on("change input propertychange", "[data-bind]", function (e) {
                    var m = $(this);
                    handle.update(m.data("bind"), m.val(), e.timeStamp);
                });
            },
            //初始化同步数据绑定-一个页面只做一次
            initSyncDataBind: function () {
                var data = this.getData();
                //console.log("initDataBind", data["initSyncState"]);
                if (data["initSyncState"] === "0") {
                    data["initSyncState"] = "1";
                    this.syncEventListen("update", data);
                }
            },
            //每次页面load都调用 初始化绑定的数据
            initDataBind: function () {
                var data = this.getData();
                for (var key in data) {
                    var triggerId = this.getTriggerKey(key);
                    this.trigger(triggerId, [key, data[key]["value"]]);
                }
            },
            //每次页面load都调用 组件数据绑定事件初始化
            dataBind: function () {
                var $this = this;
                $("[data-bind]").each(function (i) {
                    var m = $(this);
                    var key = m.data("bind");
                    var id = m.attr("id");
                    if (id !== undefined) {
                        if (!$this.getData(key)["widgetsId"][id]) {
                            $this.getData(key)["widgetsId"][id] = {
                                "id": id,
                                "type": m.prop("tagName")
                            }
                        }
                    } else {
                        throw new TypeError("数据绑定初始化，组件ID为空，请给组件设置ID属性,绑定数据:" + key + ",组件：" + m.prop("tagName"));
                    }
                    var triggerId = $this.getTriggerKey(key);
                    if (!("oninput" in m.get(0))) {
                        //处理兼容 不支持 oninput
                        if (m.is("input") || m.is("textarea")) {
                            m.on("propertychange", function (e) {
                                $this.trigger(triggerId, [key, $(this).val()]);
                            });
                        } else if (m.is("select")) {
                            m.on("change", function (e) {
                                $this.trigger(triggerId, [key, $(this).val()]);
                            });
                        }
                    }
                    //值改变触发 更新 所有绑定的控件
                    $this.on(triggerId, function (e, keyId, newVal) {
                        var $m = m;
                        if ($m.is("input") || $m.is("textarea")) {
                            if (newVal === $m.val()) {
                                return;
                            } else {
                                $m.val(newVal);
                            }
                        } else if ($m.is("select")) {
                            if (newVal === $m.val()) {
                                return;
                            } else {
                                $m.find("option[value='" + newVal + "']").attr("selected", true);
                            }
                        } else {
                            if (newVal === $m.html()) {
                                return;
                            } else {
                                $m.html(newVal);
                            }
                        }
                    });
                });
            },
            _load: function (el, url, fn) {
                this.currPageUrl = url;
                this.currPageKey = this.md5(this.currPageUrl);
                $(el).load(url, fn);
            },
            initAll: function () {
                this.sock.init();
                this.docEventBind();
            }
        },
        events = {
            callbacks: {},
            setCallback: function (el, callback) {
                if (events.callbacks[el]) {
                    return false;
                }
                if (typeof (callback) === "function") {
                    events.callbacks[el] = callback;
                }
                return true;
            },
            openModelWindow: function (options) {
                var defaults = {
                    el: "",
                    callback: undefined,
                    url: ""
                }
                $.extend(defaults, options);
                if (!events.setCallback(defaults.el, defaults.callback)) {
                    return;
                }
                var data = $.extend({}, defaults);
                data["widgetId"] = defaults.el;
                delete data["el"];
                delete data["callback"];
                handle.syncEventListen("openModelWindow", data);
            },
            eventChange: function (el, callback) {
                $(el).change(function () {
                    if (!events.setCallback(el, callback)) {
                        return;
                    }
                    //console.log("eventChange", events.callbacks[el])
                    handle.syncEventListen("change", {
                        "widgetId": el
                    });
                });
            },
            eventClick: function (el, callback) {
                $(el).click(function () {
                    if (!events.setCallback(el, callback)) {
                        return;
                    }
                    //console.log("eventClick", events.callbacks[el])
                    handle.syncEventListen("click", {
                        "widgetId": el
                    });
                });
            }
        };
    handle.initAll();


    var APP = function () {
        return this;
    }
    APP.prototype = {
        click: function (el, callback) {
            events.click(el, callback);
        },
        eventClick: function (el, callback) {
            events.eventClick(el, callback);
        },
        eventChange: function (el, callback) {
            events.eventChange(el, callback);
        },
        _initWrapper: function () {
            if (this._wrapper.el === "" || this._wrapper.url === "") {
                return;
            }
            var $this = this;
            var sival = setInterval(function () {
                if (handle.sock.state === 1) {
                    clearInterval(sival);
                    $this.load($this._wrapper.url, $this._initAll());
                }
            }, 100);
        },
        _wrapper: {el: "", url: ""},
        _created: function () {
        },
        update: function (key, value) {
            handle.update(key, value, undefined);
        },
        load: function (url, fn) {
            handle._load(this._wrapper.el, url, fn);
        },
        _setOptions: function (options) {
            this._created = options.created || function () {
            }
            if (!handle.pagesInfo[handle.getUrlKey()]) {
                handle.pagesInfo[handle.getUrlKey()] = {
                    "urlKey": handle.getUrlKey(),
                    "state": 0
                }
            }
            if (handle.pagesInfo[handle.getUrlKey()]["state"] !== 1) {
                handle.pagesInfo[handle.getUrlKey()]["state"] = 1;
                var data = options.data || {};
                if (!handle.globalData[handle.getUrlKey()]) {
                    handle.globalData[handle.getUrlKey()] = {
                        "initSyncState": "0"//初始化同步状态 0未同步
                    };
                }
                for (var key in data) {
                    var value = data[key];
                    handle.globalData[handle.getUrlKey()][key] = {
                        "value": value,
                        "widgetsId": {}
                    }
                }
            }
        },
        _initAll: function () {
            //1. 绑定数据
            //2. 触发绑定
            //3. 调用_created函数
            handle.dataBind();
            handle.initDataBind();
            handle.initSyncDataBind();
            this._created();
        }
    }

    var _APP = (function () {
        var _app;
        return function (options) {
            if (!_app) {
                _app = new APP();
                _app._wrapper = options.wrapper || {};
                _app._setOptions(options);
                _app._initWrapper();
                //console.log("创建新的APP")
            } else {
                _app._setOptions(options);
                _app._initAll();
                //console.log("使用原有的APP")
            }
            return _app;
        }
    })();

    window.App = _APP;
}(window));
